(function () {
    "use strict";

    require("./global-imports");
    var defineType = require("./define-type");
    var Notifier = require("./notifier");
    var DocumentSync = require("./document-sync");
    var node = require("./document-node");
    var doc = require("./document");
    var view = require("./view.jsx");

    function getBlockType(type) {
        switch (type) {
        case "text":
            return doc.TextBlock;
        case "image":
            return doc.ImageBlock;
        default:
            return null;
        }
    }

    var initialBlockSize = {
        width: 0.15,
        height: 0.1
    };

    var Editor = defineType(function (view, viewRef) {
        this.view = view;
        this.viewRef = viewRef;
        this.viewFrozen = false;
    }, new Notifier, {
        setViewProps: function (props) {
            if (this.viewFrozen) {
                return;
            }

            if (this.viewRef) {
                props = Object.assign(this.view.props[this.viewRef] || {}, props);
                var rootProps = {};
                rootProps[this.viewRef] = props;
                this.view.setProps(rootProps);
            } else {
                this.view.setProps(props);
            }
        }
    });

    var LayoutSelector = defineType(function (view, viewRef, layouts) {
        this.layouts = layouts;
        this.init();
    }, new Editor, {
        selectedLayout: null,
        layouts: null,

        mapLayouts: function () {
            return this.layouts.toJson();
        },

        layoutsChange: function (e) {
            var selectedIndex = this.layouts.items.indexOf(this.selectedLayout);
            this.setViewProps({
                items: this.mapLayouts(),
                selectedIndex: selectedIndex
            });

            if (selectedIndex < 0) {
                this.selectedLayout = null;
            }
        },

        selectedLayout_change: function (e) {
            this.setViewProps({selectedIndex: this.layouts.items.indexOf(this.selectedLayout)});
        },

        onRequestNew: function () {
            var layout = node.create(doc.Layout);
            this.layouts.append(layout);
            this.selectedLayout = layout;
        },

        onSelect: function (e) {
            this.selectedLayout = this.layouts.items[e.index];
        },

        onPosition: function (e) {
            if (e.newIndex === e.oldIndex) {
                return;
            }

            var selectedLayout = this.selectedLayout;
            var removed = this.layouts.splice(e.oldIndex, 1);
            this.layouts.splice(e.newIndex, 0, removed[0]);
            this.selectedLayout = selectedLayout;
        },

        init: function () {
            this.layouts.on("change", this.layoutsChange.bind(this));
            this.setViewProps({
                items: this.mapLayouts(),
                selectedIndex: this.layouts.items.indexOf(this.selectedLayout),
                onRequestNew: this.onRequestNew.bind(this),
                onSelect: this.onSelect.bind(this),
                onPosition: this.onPosition.bind(this)
            });
        }
    });

    var LayoutEditor = defineType(function () {
        this.blocksChanged = null;
        this.init();
    }, new Editor, {
        layout: null,
        isTemplate: false,

        mapBlocks: function () {
            return this.layout ? this.layout.blocks.toJson() : [];
        },

        highestZIndex: function () {
            return Math.max.apply(Math, this.layout.blocks.items.map(function (block) {
                return block.zIndex;
            }));
        },

        blockById: function (blockId) {
            var blocks = this.layout.blocks.items.filter(function (block) {
                return block.id === blockId;
            });
            return blocks.length ? blocks[0] : null;
        },

        createBlock: function (Type, x, y) {
            var block = node.create(Type);
            block.position = {
                x: x,
                y: y
            };
            block.size = {
                width: initialBlockSize.width,
                height: initialBlockSize.height
            };
            block.zIndex = this.highestZIndex() + 1;
            return block;
        },

        normalizeZIndexes: function () {
            if (!this.layout) {
                return;
            }

            this.layout.blocks.items
                .slice()
                .sort(function (left, right) {
                    if (left.zIndex < right.zIndex) {
                        return -1;
                    }

                    if (left.zIndex > right.zIndex) {
                        return 1;
                    }

                    return 0;
                })
                .forEach(function (block, index) {
                    block.zIndex = index;
                });
        },

        layoutBlocksChanged: function (e) {
            if (e.listChange) {
                this.normalizeZIndexes();
            }

            this.setViewProps({blocks: this.mapBlocks()});
        },

        layout_change: function (e) {
            if (e.oldValue) {
                e.oldValue.blocks.off("change", this.blocksChanged);
            }

            if (e.newValue) {
                e.newValue.blocks.on("change", this.blocksChanged);
            }

            this.setViewProps({
                visible: !!this.layout,
                blocks: this.mapBlocks()
            });
        },

        isTemplate_change: function (e) {
            this.setViewProps({isTemplate: this.isTemplate});
        },

        onNewBlock: function (e) {
            if (!this.layout) {
                return;
            }

            var Type = getBlockType(e.type);
            if (!Type) {
                return;
            }

            var block = this.createBlock(Type, e.x, e.y);
            this.layout.blocks.append(block);
        },

        onBlockMove: function (e) {
            if (e.moving) {
                return;
            }

            var block = this.blockById(e.blockId);
            if (!block) {
                return;
            }

            block.position = {
                x: e.x,
                y: e.y
            };
            block.zIndex = this.highestZIndex() + 1;
        },

        onBlockResize: function (e) {
            if (e.moving) {
                return;
            }

            var block = this.blockById(e.blockId);
            if (!block) {
                return;
            }

            this.viewFrozen = true;
            block.position = {
                x: e.x,
                y: e.y
            };
            block.size = {
                width: e.width,
                height: e.height
            };
            this.viewFrozen = false;
            block.zIndex = this.highestZIndex() + 1;
        },

        onTyping: function (e) {
            var block = this.blockById(e.blockId);
            block.text = e.content;
        },

        init: function () {
            this.blocksChanged = this.layoutBlocksChanged.bind(this);
            if (this.layout) {
                this.layout.blocks.on("change", this.blocksChanged);
                this.normalizeZIndexes();
            }

            this.setViewProps({
                visible: !!this.layout,
                onDelete: this.trigger.bind(this, "delete"),
                onNewBlock: this.onNewBlock.bind(this),
                onBlockMove: this.onBlockMove.bind(this),
                onBlockResize: this.onBlockResize.bind(this),
                onTyping: this.onTyping.bind(this),
                blocks: this.mapBlocks()
            });
        }
    });

    var DocumentEditor = defineType(function (view, viewRef, document) {
        this.document = document;
        this.init();
    }, new Editor, {
        document: null,
        sync: null,
        propertyEditor: null,
        templateSelector: null,
        pageSelector: null,
        layoutEditor: null,
        selectedTemplate: null,

        layoutsChange: function (e) {
            if (e.oldItems && e.oldItems.indexOf(this.layoutEditor.layout) >= 0) {
                this.layoutEditor.layout = null;
            }
        },

        selectedLayoutChange: function (selector) {
            var isTemplate = selector === this.templateSelector;
            (isTemplate ? this.pageSelector : this.templateSelector).selectedLayout = null;
            this.layoutEditor.layout = selector.selectedLayout;
            this.layoutEditor.isTemplate = isTemplate;
        },

        deleteLayout: function () {
            var layout = this.layoutEditor.layout;
            var list = layout === this.templateSelector.selectedLayout ?
                this.document.templates : this.document.pages;
            list.remove(layout);
        },

        init: function () {
            this["document.templates_change"] = this.layoutsChange.bind(this);
            this["document.pages_change"] = this.layoutsChange.bind(this);

            this.templateSelector = new LayoutSelector(this.view,
                "template-selector", this.document.templates);
            this.pageSelector = new LayoutSelector(this.view,
                "page-selector", this.document.pages);
            this["templateSelector.selectedLayout_change"] =
                this.selectedLayoutChange.bind(this, this.templateSelector);
            this["pageSelector.selectedLayout_change"] =
                this.selectedLayoutChange.bind(this, this.pageSelector);

            this.layoutEditor = new LayoutEditor(this.view, "layout-editor");
            this.layoutEditor.on("delete", this.deleteLayout.bind(this));

            this.sync = new DocumentSync(this.document);
            console.error("starting deserialization");
            this.sync.load();
            console.error("deserialization complete");
        },
    });

    module.exports.LayoutSelector = LayoutSelector;
    module.exports.LayoutEditor = LayoutEditor;
    module.exports.DocumentEditor = DocumentEditor;

    if (window) {
        window.initEditor = function () {
            var roots = document.getElementsByClassName("root");
            if (roots.length) {
                var ids = DocumentSync.list();
                var editorDoc = ids.length ?
                    new doc.Document(ids[0], 0) : node.create(doc.Document);
                window.editor = new DocumentEditor(view.createDocument(roots[0]), "", editorDoc);
            }
        };
    }
})();
